/**
 * File: FastqValidationParser.java
 * Created by: mhaimel
 * Created on: 12 Aug 2009
 * CVS:  $Id: FastqValidationParser.java,v 1.2 2009/10/09 15:15:48 mhaimel Exp $
 */
package uk.ac.ebi.curtain.util.file;

import java.io.IOException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.ac.ebi.curtain.utils.file.EntryMapper;
import uk.ac.ebi.curtain.utils.file.EntryMapperBean;
import uk.ac.ebi.curtain.utils.file.FileInfo;
import uk.ac.ebi.curtain.utils.file.impl.AbstractParser;
import uk.ac.ebi.curtain.utils.io.impl.BufferedRaf;

/**
 * @author mhaimel
 *
 */
public class FastqValidationParser extends AbstractParser {
	
	private Log log = LogFactory.getLog(this.getClass());
	
	public void parseNEntry(int nCnt, BufferedRaf in, FileInfo info,EntryMapper mapper) throws IOException {
		int lineCnt = 0;
		int iCnt = 0;
		String id = StringUtils.EMPTY;
		String header = StringUtils.EMPTY;
		String annot = StringUtils.EMPTY;
		StringBuilder seq = new StringBuilder();	
		StringBuilder qual = new StringBuilder();	
		String line = StringUtils.EMPTY;

		long nextPos = in.getFilePointer();
		long prevPos = nextPos;
		long currEntryPos = nextPos;
		boolean reset = false;
		boolean submit = true;
		
		while((nCnt < 0 || nCnt > iCnt) && (line = in.readLine2()) != null){
			prevPos = nextPos;
			nextPos = in.getFilePointer();
			++lineCnt;
			switch ((lineCnt%4)) {
			case 1: // header
				currEntryPos = prevPos;
				int wsIdx = line.indexOf(' ');
				if(wsIdx > 0){
					id = line.substring(0,wsIdx);
					header = line.substring(wsIdx).trim();					
				} else {
					header = StringUtils.EMPTY;
					id = line;
				}
				// remove @ from id
				if(id.charAt(0) == '@'){
					id = id.substring(1);
				} else {
					getLog().error("@ expected in id line " + id + " at file pointer: " + prevPos + " at " + info);
				}
				if(skipId(info,id,currEntryPos)){
					lineCnt += skipLines(3,in);
					reset=true;					submit=false;
				}
				break;
			case 2:// sequence
				seq.append(line);
				if(skipExtraAnnotation()){
					lineCnt += skipLines(2,in);
					submit=true;
					reset=true;
				}
				break;
			case 3:// extra annotation
				annot = line.substring(1);
				break;
			case 0:// Quality line
				qual.append(line);
				submit=true;
				reset=true;
				break;
			}
			if(submit){
				submit = false;
				if(addValid(currEntryPos,info,mapper, id, header, seq, annot,qual)){
					++iCnt;
				}
			}
			if(reset){
				reset = false;
				// reset values
				id = StringUtils.EMPTY;
				header=StringUtils.EMPTY;
				seq.delete(0, seq.length());
				qual.delete(0, qual.length());
				annot = StringUtils.EMPTY;
				if(iCnt>0 && iCnt%1000000 == 0 ){
					getLog().debug(iCnt + " ...");
				}
			}
			nextPos = in.getFilePointer();
		}
	}

	protected boolean skipExtraAnnotation() {
		return false;
	}

	protected int skipLines(int skipN, BufferedRaf in) throws IOException {
		int iLines = 0;
		while (iLines<skipN) {
			switch (in.read()) {
			case -1:
			case '\n':
				++iLines;
				break;
			case '\r':
				++iLines;
				long cur = in.getFilePointer();
				if ((in.read()) != '\n') {
					in.seek(cur);
				}
				break;
			default:
				break;
			}
		}
		return iLines;
	}

	protected boolean skipId(FileInfo info, String id, long filePos) {
		return false;
	}

	private boolean addValid(long pos, FileInfo info, EntryMapper mapper, String id, String header, StringBuilder seq, String annot, StringBuilder qual) {
		if(StringUtils.isNotBlank(id)
				&& null != header
				&& seq.length() > 0){
			mapper.mapEntry(buildEntryBean(pos, info, id, header, seq, annot, qual));
			return true;
		} else {
			return false;
		}
	}

	protected EntryMapperBean buildEntryBean(long pos, FileInfo info, String id,String header, StringBuilder seq, String annot, StringBuilder qual) {
		return new EntryMapperBean(pos,info,new String(id), new String(header), seq.toString(), annot,qual.toString());
	}
	
	private Log getLog(){
		return log;
	}

}
